#include "sct/rtspserver/rtspserver.h"


namespace sct
{
void* CRTSPServer::rtsp(void * arg)
{
	CRTSPServer* self = (CRTSPServer*)arg;

	self->mEnv->taskScheduler().doEventLoop(&self->mState);
	return NULL;
}


CRTSPServer::CRTSPServer(const int port):
		mThdserver(-1),
		mState(Terminated),
		mPort(554),
		mEnv(NULL),
		mServer(NULL),
		mSched(NULL)
{
	;
}

CServerMediaSession* CRTSPServer::createServerMediaSession(
				const char* path)
{
	std::list<CServerMediaSession*>::iterator session;
	for(session = mSessions.begin();
			session != mSessions.end();
			session++)
	{
		if(path == (*session)->path)
		{
			break;
		}
	}
	if(session == mSessions.end())
	{
		CServerMediaSession * item = new CServerMediaSession;
		item->path = path;
		item->server_media_session = NULL;

		mSessions.push_back(item);
		return item;
	}

	return NULL;
}

CServerMediaSubsession* CRTSPServer::createServerMediaSubsession(
			CServerMediaSession* session, const CStreamInfo & info)
{
	CServerMediaSubsession * subsession = new CServerMediaSubsession;

	subsession->source = NULL;
	subsession->type = info.type;

	session->subsessions.push_back(subsession);
	return subsession;
}

void CRTSPServer::init()
{
	BasicTaskScheduler* sched = BasicTaskScheduler::createNew();
	BasicUsageEnvironment* env = BasicUsageEnvironment::createNew(*sched);
	CMyRTSPServer* myServer = NULL;

	mEnv = env;
	mSched = sched;

	while(1)
	{
		myServer = CMyRTSPServer::createNew(
			*env, mPort, NULL, 15);
		if(NULL == myServer)
		{
			SCT_LOGD("create rtsp server failed by port:%d", mPort);
			if(mPort < 8554)
			{
				mPort = 8553;
			}

			mPort++;
			if(mPort > 9554)
			{
				SCT_LOGD("create rtsp server failed exit");
				exit(-1);
			}
			continue;
		}

		break;
	}

	mServer = myServer;
	SCT_LOGD("rtsp:port:%d", mPort);

	char const* descriptionString = "Session streamed by SCT";
	std::list<CServerMediaSession*>::iterator itsession;
	for(itsession = mSessions.begin();
			itsession != mSessions.end();
			itsession++)
	{
		(*itsession)->server_media_session = ServerMediaSession::createNew(
				*env,
				(*itsession)->path.c_str()+1,
				(*itsession)->path.c_str()+1,
				descriptionString);
		myServer->addServerMediaSession( (*itsession)->server_media_session );

		initServerMediaSubsession(*itsession);
	}

	mState = Running;
	pthread_create(&mThdserver, NULL, rtsp, this);
}

void CRTSPServer::initServerMediaSubsession(CServerMediaSession*session)
{
	std::list<CServerMediaSubsession*>::iterator itsub;
	for(itsub=session->subsessions.begin();
			itsub!=session->subsessions.end();
			itsub++)
	{
		CServerMediaSubsession*sub=*itsub;

		switch( sub->type )
		{
		case H264:
		{
			sub->source = new CH264StreamSource(this);
			session->server_media_session->addSubsession(new sct::CH264VideoServerMediaSubsession
					(*mEnv, sub->source, true)
					);
			break;
		}

		case AAC:
		{
			sub->source = new CAACStreamSource(this);
			session->server_media_session->addSubsession(sct::CAACServerMediaSubsession::createNew(
					*mEnv, sub->source, true));
			break;
		}

		case AMR:
		{
			sub->source = new CStreamSource(this);
			session->server_media_session->addSubsession(sct::CAMRServerMediaSubsession::createNew(
					*mEnv, sub->source, true));
			break;
		}

		case TS:
		{
			sub->source = new CTsStreamSource(this);
			session->server_media_session->addSubsession(sct::CTsServerMediaSubsession::createNew(
					*mEnv, sub->source, true));
			break;
		}

		default:
		{
			SCT_LOGD();
			break;
		}
		}
	}
}

const char* CRTSPServer::getUrl(CServerMediaSession* session)
{
	std::string ip = "0.0.0.0";
	char * fbuf = new char[4096];
	char* buf = new char[200];
	const int mx = 200;

	do
	{
		int fd1 = open("/proc/net/dev", O_RDONLY);
		if(fd1<0)
		{
			SCT_LOGD("open failed");
			break;
		}

		int rbytes = read(fd1, fbuf, 4096);
		if(rbytes<0)
		{
			SCT_LOGD("read failed");
			close(fd1);
			break;
		}
		close(fd1);

		if(4096 == rbytes)
		{
			SCT_LOGD("the buff is too small, may cause error");
		}

		std::istringstream fin(fbuf);
		int fd = -1;
		fd = socket(AF_INET, SOCK_DGRAM, 0);
		if(fd < 0)
		{
			SCT_LOGD();
			break;
		}

		fin.getline(buf, mx);
		fin.getline(buf, mx);

		while(fin.good())
		{
			fin.getline(buf, mx);
			std::istringstream sin(buf);
			std::string name;
			sin >> name;

			if(!name.empty())
			{
				name = name.substr(0, name.find_first_of(':'));

				struct ifreq dev;
				strcpy(dev.ifr_name, name.c_str());
				int ret;
				ret = ioctl(fd, SIOCGIFFLAGS, &dev);
				if(ret < 0)
				{
					continue;
				}

				short flag;
				flag = dev.ifr_flags;

				if(IFF_RUNNING != (flag&IFF_RUNNING))
				{
					continue;
				}


				ret = ioctl(fd, SIOCGIFADDR, &dev);

//				SCT_LOG("%s %d", name.c_str(), ret);
				if(0 == ret)
				{
					struct sockaddr_in addr;
					memcpy(&addr, &dev.ifr_addr, sizeof(addr));

					uint32_t uip = addr.sin_addr.s_addr;
					if(0 == uip || 0x100007f == uip)
					{
						continue;
					}

					uint8_t*p = (uint8_t*)&uip;
					sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

					ip = buf;

					break;
				}
			}
		}

	}while(0);


	SCT_DELETE_ARRAY(fbuf);
	SCT_DELETE_ARRAY(buf);

	std::ostringstream oStr;
	oStr << "rtsp://" << ip << ":" << mPort  << session->path;
	session->url = oStr.str();

	return session->url.c_str();
}

void CRTSPServer::putMediaData(CServerMediaSubsession* subsession,
		const void*p, const int len, const int64_t pts)
{
	subsession->source->putdata(p, len, pts);
}

CRTSPServer::~CRTSPServer()
{
	mState = Terminated;
	pthread_join(mThdserver, NULL);

	while(!mSessions.empty())
	{
		CServerMediaSession* session = mSessions.front();
		mSessions.pop_front();

		if(session->server_media_session)
		{
			mServer->closeAllClientSessionsForServerMediaSession(session->server_media_session);
			session->server_media_session->deleteAllSubsessions();
			mServer->deleteServerMediaSession(session->server_media_session);
		}

		while(!session->subsessions.empty())
		{
			CServerMediaSubsession* sub = session->subsessions.front();
			session->subsessions.pop_front();

			SCT_DELETE(sub->source);
			SCT_DELETE(sub);
		}

		SCT_DELETE(session);
	}

	CMyRTSPServer::close(mServer);
	mEnv->reclaim();
	SCT_DELETE(mSched);
}


CMyRTSPServer::~CMyRTSPServer()
{
	SCT_LOGD("%d client while delete server", mRef);;
}


CMyRTSPServer::CMyRTSPClientSession::CMyRTSPClientSession(CMyRTSPServer& ourServer, u_int32_t sessionId):
		RTSPClientSession(ourServer, sessionId)
{
	mServer = &ourServer;
	mServer->mRef++;

	SCT_LOGD("add CMyRTSPClientSession:%p %d\n", this, mServer->mRef);
}

CMyRTSPServer::CMyRTSPClientSession::~CMyRTSPClientSession()
{
	mServer->mRef--;

	SCT_LOGD("delete CMyRTSPClientSession:%p %d\n", this, mServer->mRef);
}


GenericMediaServer::ClientSession* CMyRTSPServer::createNewClientSession(u_int32_t sessionId)
{
	return new CMyRTSPClientSession(*this, sessionId);
}


CMyRTSPServer::CMyRTSPServer(UsageEnvironment& env,
		     int ourSocket, Port ourPort,
		     UserAuthenticationDatabase* authDatabase,
		     unsigned reclamationSeconds):
		    		 RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds)
{
	mRef = 0;
}

CMyRTSPServer* CMyRTSPServer::createNew(UsageEnvironment& env, Port ourPort,
			      UserAuthenticationDatabase* authDatabase,
			      unsigned reclamationSeconds)
{
	  int ourSocket = setUpOurSocket(env, ourPort);
	  if (ourSocket == -1) return NULL;

	  return new CMyRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds);
}

}
